DevTool Demo Virtual IMU

This demo is a comprehensive example of how to implement custom development within SOLIS using the MAX Development Tool. It walks through step-by-step instructions for creating a new custom component that could be used as a replacement for a broken IMU.

Overview

SOLIS is built with a modular and flexible design. When combined with the MAX Development Tool, SOLIS supports customization by running MAX Flight Software with ODySSy to generate simulation data and then using the MAX Development Tool to rapidly and reliably write code to run within MAX.

This tutorial will cover

  • Fundamental concepts of how SOLIS, MAX, and the MAX DevTool interact.
  • How to use the MAX DevTool to get custom logic available in MAX.
  • How to configure SOLIS to create, connect, and configure the custom logic.
  • How to run the custom logic, get telemetry from it, and send commands to it.

To complete this tutorial, you will need

  • STK SOLIS 12 (or later)
  • Visual Studio 2017 (or later) with SDK installed
  • Basic knowledge of C++

Demonstration Example & Development Process

Tutorial Example
  • An IMU is malfunctioning, and you’ve been tasked with developing an algorithm to replace this sensor dependency.
  • You wish to develop and test this within SOLIS.
Tutorial Notes
  • The purpose of this tutorial is to demonstrate the concepts that are required to develop custom logic; not to arrive at a fully-robust algorithm to meet the objective.
  • With this knowledge, you will be well on your way to solving any customization idea.
  • SOLIS and MAX are very valuable tools for developing such an algorithm, due to the rapid cycles of design, implementation, and testing.
    • This is true regardless of the final Flight Software chosen to implement within.
The Custom Development Process

Custom Development Process

Creating a Project

Demo Directories

A completed instance of this demo is provided in the delivered SOLIS Example Scenarios

  • Distributed via the SOLIS 12.0 installer C:\ProgramData\AGI\STK 12\SOLIS\ExampleScenarios\SOLISExample_DevToolDemo_Virtual_IMU

To begin a new instance of this demo

  1. Make the new directory: C:\ProgramData\AGI\STK 12\SOLIS\DevToolDemo_Virtual_IMU
  2. Copy the contents of: C:\ProgramData\AGI\STK 12\SOLIS\ExampleScenarios\SOLISExample_DevToolDemo_Virtual_IMU\_FreshDemoContent into C:\ProgramData\AGI\STK 12\SOLIS\DevToolDemo_Virtual_IMU
  3. Open STK Scenario: C:\ProgramData\AGI\STK 12\SOLIS\DevToolDemo_Virtual_IMU\SOLISExample_Landmapper_BC.sc
  4. Open SOLIS on the Landmapper_BC satellite within this scenario

The DevTool will rapidly and reliably write code to run within MAX.

Open the DevTool

In SOLIS click the DevTool Start Button button to get started. Default values are all that is needed for this project.

Open DevTool

Save setting to RunDevTool_Virtual_IMU.bat file for easy running of the DevTool next time.

Open DevTool

Run the DevTool

Click on the Run DevTool button to run the DevTool. This will generate a Visual Studio solution, ready for your custom development.

Using the saved .bat is equivalent to clicking the Run DevTool button in the UI.
Build Project and Refresh SOLIS

The solution will build successfully “out of the box"

Open DevTool

Open DevTool

After building, hit Refresh Window Button on the SOLIS GUI and you will see the DLL with a timestamp. This is an indication that SOLIS is linked with the Virtual_IMU.

Open DevTool

Step 1: Conceptual Design

  • With a potentially bad IMU, we’d like to have the ability to remove this dependency in the system.
  • We will create a new block called Virtual_IMU.
    • By having this block produce the same interface as IMU Processing, we can simply connect the output of Virtual IMU to the Attitude Determination.
    • We will also connect the original IMU Processing interface to the Virtual IMU, so that we can continue to pass that signal to Attitude Determination (if desired).
  • Virtual_IMU will get attitude quaternion data from the Star Tracker. This information can be “back-differenced” to form an estimate of spacecraft rates.
    • We know this quaternion information is going into the Attitude Determination (Kalman Filter), so we will utilize that same interface to get the data into Virtual_IMU.
  • We will build some commands, telemetry, and parameters into Virtual_IMU to show those concepts.

DevTool xml Conceptual Design

This implementation will have some known issues, such as when the Star Tracker is not reporting valid measurements due to Earth obscuration. There are many ways to address these issues to create a more robust system, such as: incorporating other sensor measurements or propagating on expected torques. These items are beyond the scope of this example, but it should be clear that the DevTool is an excellent work-horse for exploring and proving these designs.

Step 2: Translation of design to XML

With the concept identified; it’s time to investigate how it fits with the existing system. To determine where to start, we will look at the MAX ADCS_Sensors.xml found in C:\Program Files\AGI\STK 12\Solis\ExampleMAXDevToolXML to find what interface the IMU Processing produces.

Finding the IMU Interface

A search for “IMU” reveals the FSW_SenProc_IMU interface.

  • This interface embeds the FSW_SenProc_RTE interface, which we see contains a VECTOR of Rate_BDY_rdps.

ADCS Sensor XML

To confirm FSW_SenProc_RTE is what we want, a search for “Determination” in ADCS_FSW.xml shows this is the connection to the AD.

ADCS Sensor XML

Adding the RTE Interface

Recalling from our conceptual design, we’d like our new Virtual_IMU to produce this RTE interface. This is done using the <BaseInterface Name="FSW_SenProc_RTE"/> tag.

<?xml version="1.0" encoding="utf-8"?> <Extension xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="MAXDevToolSchema.xsd" xmlns="asi_mc"> <SettingsDefinition> <FileHeader> ============================================================================ A custom algorithm demonstration ============================================================================ </FileHeader> </SettingsDefinition> <ComponentDefinition Name="Virtual_IMU"> <BaseInterface Name="FSW_SenProc_RTE"/> </ComponentDefinition> </Extension>

Run the DevTool using the RunDevTool_Virtual_IMU.bat

You should now see new files in your Virtual_IMU solution. Around line 141 within Virtual_IMU.cpp you will see the return function that will provide rate data across our FSW_SenProc_RTE interface.

Virtual IMU CPP

Bringing Star Tracker Data into the Virtual IMU

Using the search concepts from adding the RTE Interface, we bring Star Tracker Data into the Virtual IMU using the <Configuration><Interface> Tag in the XML.

We are looking for the CBI2BDY (Central Body Inertial to Body) quaternion from the star tracker, which is part of the FSW_SenProc_ADSensor interface produced by FSC_SenProc_STR

Write the XML shown below and run the RunDevTool_Virtual_IMU.bat

Start of file:

<?xml version="1.0" encoding="utf-8"?> <Extension xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="MAXDevToolSchema.xsd" xmlns="asi_mc"> <SettingsDefinition> <FileHeader> ============================================================================ A custom algorithm demonstration ============================================================================ </FileHeader> </SettingsDefinition> <ComponentDefinition Name="Virtual_IMU"> <BaseInterface Name="FSW_SenProc_RTE"/>

Add <Configuration> and <Interface>:

<Configuration> <Interface InterfaceType="FSW_SenProc_ADSensor" Description="Pull in the CBI2BDY quaternion from the Star Tracker"/> </Configuration>

Rest of the file:

</ComponentDefinition> </Extension>

Access to our Star Tracker data within the Virtual IMU

Within Virtual_IMU.cpp, you should now be able to access the m_Connection structure.

If this does not immediately show up within Intellisense, run Project/Rescan Solution, and compile.

Write the following two lines, providing access to our Star Tracker data within the Virtual IMU component

bool str_Valid = m_Connection.FSW_SenProc_ADSensor_ptr->GetIsValid(); QUATERNION str_QCBI2BDY = m_Connection.FSW_SenProc_ADSensor_ptr->GetSensedQuaternion_CBI2BDT();

Virtual IMU CPP

Full XML Translation

Example XML

<?xml version="1.0" encoding="utf-8"?> <Extension xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="MAXDevToolSchema.xsd" xmlns="asi_mc"> <SettingsDefinition> <FileHeader> ============================================================================ A custom algorithm demonstration ============================================================================ </FileHeader> </SettingsDefinition> <ComponentDefinition Name="Virtual_IMU"> <BaseInterface Name="FSW_SenProc_RTE"/> <!-- A local definition of an enumeration that will be used in parameters, commands, and telemetry --> <DefinedEnum Name="ERateSource"> <Enum Value="0" Text="IMU"/> <Enum Value="1" Text="Virtual_IMU"/> </DefinedEnum> <!-- This section represents all things that are set from configuration files --> <Configuration> <Interface InterfaceType="FSW_SenProc_ADSensor" Description="Pull in the CBI2BDY quaternion from the Star Tracker"/> <Interface InterfaceType="FSW_SenProc_RTE" Description="Pull in the original IMU data"/> <EnumParameter Name="E_InitialRateSource" Enum="ERateSource" DefaultValue="0" Description="Initial rate source to use"/> <BasicParameter Name="D_MaxDeltaTimeForRate_s" Type="double" Units="sec" MinValue="0.0" DefaultValue="10.0" Description="Maximum time between STR measurements to compute the back-difference"/> </Configuration> <Commands> <Command Mnemonic="SETSOURCE" Description="Set the source of data to pass through"> <ComponentEnumArgument Name="Source" DefinedEnum="ERateSource" Description="Which source to pass through"/> </Command> </Commands> <Telemetry> <ComponentEnumItem Name="RateSource" DefinedEnum="ERateSource" Units="enum" Description="The currently selected rate source"/> <!-- Note: All telemetry is a single channel. The [X:Y:Z] syntax below is a macro to form 3 telemetry mnemonics (doubles) with one line of XML on the VECTOR--> <BasicItem Name="Rate_BDY[X:Y:Z]" LocalName="Rate_BDY_rdps" Type="VECTOR" Units="rad/s" Description="The computed virtual rate in the BDY [X:Y:Z] frame"/> </Telemetry> </ComponentDefinition> </Extension>

XML Schema Used
  • Extension The root-level XML node, containing any number of component interface and/or definition descriptions.
    • SettingsDefinition Optional settings
    • ComponentDefinition A Core building block of adding software to augment MAX.
      • BaseInterface Defines the interface(s) that the Component will produce. In this case, we are producing the FSW_SenProc_RTE interface which will be used to send rate data to Attitude Determination.
      • DefinedEnum Defines an enumeration local to this component. We will utilize this enumeration within a parameter, command, and telemetry, as we switch the source of the rate signal output.
      • Configuration
        • Interface Defines the interface(s) that this component needs to get data from. In this case, we are getting data from the IMU and the Star Tracker.
        • EnumParameter & BasicParameter Used to create parameters into the system that are defined within the Configuration files (*.cfg).
      • Commands Defines the commands. In this case, we have one command that has an input argument to change the rate source.
      • Telemetry Defines the telemetry data (to STK Data Provider and/or to CSV) from this component.
This example is only scratching the surface. The DevTool can create an enormous variety of highly-effective code, with confidence that it will work right. ASI has created it’s entire ADCS system with the DevTool and has been using it as the sole source of code development since 2016.

Step 3: Implement logic in the Virtual IMU

Now that the DevTool has written all the framework, it’s time to start implementing logic.

Basics First
The code in BLUE requires implementing, the others are already done by the DevTool.

Virtual_IMU.hpp

We will need to define a couple variables, in the “private” section.

double m_PrevTime_s; QUATERNION m_PrevQ_CBI2BDY;

Virtual_IMU.cpp

Initialize the new variables.

void CVirtual_IMU::CommonConstructor(void) { m_PrevQ_CBI2BDY = C_QId; m_PrevTime_s = 0.0; }

At the bottom of Init() – Initialize the RateSource telemetry to the initial parameter.

m_Tlm.RateSource = m_Parm.E_InitialRateSource;

Within GetRate_BDY_rdps() – Return the computed rate across the FSW_SenProc_RTE interface.

const VECTOR CVirtual_IMU::GetRate_BDY_rdps(void) { return m_Tlm.Rate_BDY_rdps; }

Within GetMeasRateValid() – Return rate always valid (simplified assumption for now)

Hardware::E_SenMeasValidity CVirtual_IMU::GetMeasRateValid(void) { return Hardware::E_ValidThisFrame; }

Within AcceptCmd_SETSOURCE() – Update the RateSource to the command argument

E_FSW_STATUS CVirtual_IMU::AcceptCmd_SETSOURCE(const SETSOURCE_CMD_STRUCT* arguments ) { m_Tlm.RateSource = arguments->Source; return E_FSW_OK; }

The primary algorithm will be implemented in the ProcessInputs() function

Primary Algorithm

int CVirtual_IMU::ProcessInputs(void) { int Ret = 0; // Setup a switch statement that either passes the raw IMU signal through, or computes the rate from the Star Tracker. switch (m_Tlm.RateSource) { case ERateSource::E_IMU: // In this case, just pass through what the IMU reported m_Tlm.Rate_BDY_rdps = m_Connection.FSW_SenProc_RTE_ptr->GetRate_BDY_rdps(); break; case ERateSource::E_Virtual_IMU: // This is where we will perform calculations to form a rate estimate from the Star Tracker // If the Star Tracker is valid, get the data from it to use for upcoming calculations. if (m_Connection.FSW_SenProc_ADSensor_ptr->GetIsValid()) // Star Tracker has a new measurement { QUATERNION str_Q_CBI2BDY = m_Connection.FSW_SenProc_ADSensor_ptr->GetSensedQuaternion_CBI2BDY(); double str_CurrentTime_s = m_Connection.FSW_SenProc_ADSensor_ptr->GetLastValidTime_SCLK_s(); double thisDeltaTime_s = str_CurrentTime_s - m_PrevTime_s; // If we have a new measurement that is within a parameterized time of an old good measurement, compute the quaternion difference and get the rate. if (thisDeltaTime_s < m_Parm.D_MaxDeltaTimeForRate_s && thisDeltaTime_s > EPSILON) { QUATERNION qDelta; Math_Qinv_Q_Mult(m_PrevQ_CBI2BDY, str_Q_CBI2BDY, qDelta); // Calculate Delta Angles using small-angle approximation VECTOR deltaAng_rd = 2.0 * Math_Sign(qDelta[3]) * VECTOR(qDelta[0], qDelta[1], qDelta[2]); // Determine Rate by back-differencing the quaternion m_Tlm.Rate_BDY_rdps = deltaAng_rd / thisDeltaTime_s; } // Setup the persistence variables for the next back-difference m_PrevTime_s = str_CurrentTime_s; m_PrevQ_CBI2BDY = str_Q_CBI2BDY; } break; } return Ret; }

This algorithm could be expanded-upon or modified in many ways. The tutorial will stop here with further logic implementation, but you are encouraged to continue additional exploration.
We now have a DLL that represents our intended block. We have not yet created an instance of that block, or connected it to other blocks. Simulink Analogy: We have a block in our library, but it is not part of our model yet

Step 4: Create, Connect, and Configure the Virtual IMU

To configure our simulation to use our new Virtual_IMU, we use the Configuration Manager.

Create

Create a Copy of our SOLIS-Generated Configuration Set, named WithVirtual_IMU

Create Configuration

Create an instance of our Virtual_IMU component, configured as-shown

  • Priority 315 defines when this component executes, relative to other priorities. We place it between FSW_SenProc_IMU (310) and FSW_AttDetermination (600). The Configuration Overview button provides a table of all Components and their priorities.
  • Master ID 315 defines the command and telemetry APID (Application ID). This must be unique from other APIDs
  • Be sure the Output File goes into FSW_Components.cfg. An error would occur if this goes into the ODY_* cfg files

Create Configuration

Connect

Add (or edit) Connections to the Virtual_IMU

  • We want the Virtual_IMU to get data from the FSW_SenProc_IMU and FSW_SenProc_STR_Sinclair_STR.
The GUI knows what connections are possible, and what sources are producing a connection that will fit.

Similar to the Connections section, the Parameters can be defined

  • If not specified, they will default to the DefaultValue specified in the XML.
  • If the XML did not specify a DefaultValue, the value will be zero.

Connections

Configure

Since we need to modify the Attitude Determination, create a Copy of the FSW_AttDetermination

  • The default copy parameters are good, just change the Version Name to WithVirtual_IMU.

Copy FWS AttDetermination

Now we need to modify the Connection_FSW_SenProc_RTE

  • Select the 2nd row in the Connections section.
  • Click the edit icon.
  • Change the Connected Component to Virtual_IMU.
  • Change the Accessor to FSW_SenProc_RTE.
    • Not Connection_FSW_SenProc_RTE, as this is the accessor to the input IMU.

Modify Connection

Select WithVirtual_IMU as the Active Configuration Set

  • This tells SOLIS to write the Configuration files as you’ve set them up within that set, with our new Virtual IMU.

Add the new Custom/Virtual_IMU packet to the recorded telemetry

Custom Packet

Running the Simulation

At this point the system is fully-configured to run the new logic.

Simulation
  • Start the simulation running 10x realtime
  • Using the Real-Time Commanding feature, issue our new Custom command to change the Rate Source
  • Next, issue a Slew Relative command to cause the Star Tracker to point toward the Earth
  • Lastly, run the GoTo Hold command timed while the Star Tracker Field of View is not intersecting Earth

Custom Command Custom Development Process

Telemetry Results

Telemetry results can be seen using the STK Data Provider. All SOLIS data will be under the User Supplied Data section, with our new Virtual IMU telemetry under the Custom folder.

Below shows the results of this simulation. At ~152 seconds the command was issued to start using the Virtual_IMU rate solution. This held together until the slew was commanded that now blocked the star tracker. The system ultimately recovered after the GoTo Hold command was issued at ~450 sec.

Simulation Results

Possible things to try from here

  • Have your Virtual_IMU DLL compiled for Debug
    • While the simulation is running, attach the Debugger to MAX.exe
    • Set breakpoints and step through the logic
  • Design logic that is more robust to star tracker outages, such as implementation of feed-forward propagation on expected torques
  • Add an additional Star Tracker, and associated logic
  • Create a fault within the IMU (modify a parameter on the OSC_SenMdl_IMU using MISC_CONFIG_PARSE)
    • Fault example MISC_CONFIG_PARSE(“OSC_SenMdl_IMU_MPU.B_IsOn = 0;”)
    • Create a telemetry monitor that can detect this
    • Create an automated response that switches the system to use the Virtual
    • Try to resolve a robust solution to get the system safely pointed at the Sun
  • Create telemetry that compares the IMU to the Virtual IMU
  • Modify the timeout parameters
  • Start working an alternate customization idea…